""""
main.py 的执行路径可分为以下几个关键阶段：
一、入口函数 main()
    if __name__ == "__main__":
        main()
    程序启动后首先进入 main() 函数
    调用 get_user_input() 获取测试配置参数
二、配置参数获取阶段
    def get_user_input():
        ...
        if config and messagebox.askyesno("使用上次配置", "是否使用上次的配置？"):
            return tuple(config.values())
        ...
        优先尝试从 config.json 加载历史配置
        通过 Tkinter 创建 GUI 输入界面收集以下参数：
        platformName（平台名称）
        platformVersion（平台版本）
        appPackage（应用包名）
        appActivity（启动 Activity）
        deviceName（设备名称）
        测试用例表格路径
        报告标题
三、测试执行准备阶段
        def run_tests_in_thread(inputs, progress_callback, completion_callback):
        ...
        创建后台线程避免阻塞主线程
        初始化 AndroidUITest 实例
        设置进度回调函数
四、测试套件执行阶段
        def run_test_suite(self, excel_path, platform_name, platform_version, app_package, app_activity, device_name, report_title):
            ...
        4.1 读取测试用例
            test_cases = self.read_test_cases(excel_path)
            从 Excel 文件读取所有 sheet 的测试用例数据
        4.2 初始化 Appium 驱动
            self.setup_driver(platform_name, platform_version, app_package, app_activity, device_name)
            通过 driver_helper.py 创建 Appium 会话
        4.3 执行测试用例
            case_results = self._execute_test_cases(test_cases)
            按以下流程执行每个用例：
            检查是否执行标志（Y/N）
            根据关键字映射执行操作（点击/输入/验证等）
            记录测试结果（成功/失败/跳过）
            失败时自动截图
        4.4 生成测试报告
            report_path = self._generate_and_open_report(...)
            使用 report_generator.py 生成 HTML 报告并自动打开
五、进度可视化阶段
        def create_progress_window():
            ...
        创建包含以下组件的进度窗口：
        进度条（Progressbar）
        状态标签（Label）
        日志文本框（Text）
        关闭按钮（Button）
        实时更新进度信息和日志输出
六、清理阶段
        finally:
            if self.driver:
                self.driver.quit()
            确保测试结束后释放 Appium 驱动资源
            保存最新配置到 config.json

完整执行流程图：
[启动main()]
   ↓
[加载配置/获取输入]
   ↓
[创建进度窗口]
   ↓
[初始化Appium驱动]
   ↓
[读取Excel用例]
   ↓
[循环执行测试用例]
   ↓
[生成HTML报告]
   ↓
[清理资源/结束]
"""

import subprocess
import time
import tkinter as tk
from tkinter import Label, Entry, Button, messagebox, ttk
import json
from colorama import init, Fore, Style
import webbrowser
import os
import threading

from core.test_base import TestBase
from core.test_result import TestResult
from utils.report_generator import ReportGenerator
from utils.driver_helper import *


class AndroidUITest(TestBase):
    """
    Android UI 自动化测试主类
    继承自 TestBase 基类，实现基于 Appium 的移动端 UI 测试框架
    提供完整的测试用例执行流程：初始化、测试执行、结果记录和报告生成
    """

    def __init__(self):
        """
        初始化测试环境
        初始化颜色输出模块，创建测试结果存储列表
        """
        super().__init__()
        init()
        self.test_results = []
        self.progress_callback = None  # 用于更新进度的回调函数

    def set_progress_callback(self, callback):
        """设置进度更新回调函数"""
        self.progress_callback = callback

    def update_progress(self, message):
        """更新进度信息"""
        if self.progress_callback:
            self.progress_callback(message)

    def read_test_cases(self, excel_path):
        """
        从 Excel 文件中读取测试用例数据

        参数:
            excel_path (str): Excel 文件路径

        返回:
            DataFrame: 包含测试用例的 pandas 数据框，读取失败返回 None
        """
        try:
            import pandas as pd
            # 读取所有sheet表并合并，跳过每个sheet的第一行
            test_cases = pd.read_excel(excel_path, sheet_name=None, header=0)
            combined_df = pd.DataFrame()
            for sheet_name, df in test_cases.items():
                df['SheetName'] = sheet_name  # 添加sheet名称列
                combined_df = pd.concat([combined_df, df], ignore_index=True)
            return combined_df
        except Exception as e:
            logging.error(f"读取测试用例失败: {str(e)}")
            self.update_progress(f"读取测试用例失败: {str(e)}")
            return None

    def run_test_suite(self, excel_path, platform_name, platform_version, app_package, app_activity, device_name,
                       report_title):
        """
        运行整个测试套件

        参数:
            excel_path (str): 测试用例文件路径
            platform_name (str): 设备平台名称(Android/iOS)
            platform_version (str): 平台版本号
            app_package (str): 被测应用包名
            app_activity (str): 启动 Activity 名称
            device_name (str): 设备名称
            report_title (str): 测试报告标题

        返回:
            None
        """
        try:
            self.update_progress("开始读取测试用例...")
            test_cases = self.read_test_cases(excel_path)
            if test_cases is None:
                print(f"{Fore.RED}读取测试用例失败{Style.RESET_ALL}")
                self.update_progress("读取测试用例失败")
                return

            # 初始化 Appium 驱动
            self.update_progress("正在初始化Appium驱动...")
            self.setup_driver(platform_name, platform_version, app_package, app_activity, device_name)

            # 启动被测应用
            self.update_progress("正在启动应用...")
            subprocess.run(f'adb shell am start -n {app_package}/{app_activity}', shell=True,
                           capture_output=True, text=True)

            # 执行所有测试用例
            self.update_progress("开始执行测试用例...")
            case_results = self._execute_test_cases(test_cases)

            # 统计测试结果
            success_cases, failed_cases, skipped_cases = self._count_test_results(case_results)

            # 打印测试摘要信息
            self._print_test_summary(success_cases, failed_cases, skipped_cases)

            # 生成并打开测试报告
            self.update_progress("正在生成测试报告...")
            report_path = self._generate_and_open_report(success_cases, failed_cases, skipped_cases, report_title)
            self.update_progress("测试完成!")

        except Exception as e:
            print(f"{Fore.RED}测试执行失败: {str(e)}{Style.RESET_ALL}")
            self.update_progress(f"测试执行失败: {str(e)}")
            logging.error(f"测试执行失败: {str(e)}")
        finally:
            if self.driver:
                self.driver.quit()

    def _execute_test_cases(self, test_cases):
        """
        执行所有测试用例

        参数:
            test_cases (DataFrame): 包含测试用例的数据框

        返回:
            dict: 测试用例执行结果字典，格式为 {case_id: {'status': status, 'skipped': skipped}}
        """
        case_results = {}
        current_sheet = None
        total_cases = len(test_cases)
        processed_cases = 0

        for index, test_case in test_cases.iterrows():
            # 更新进度
            processed_cases += 1
            progress_percent = (processed_cases / total_cases) * 100
            self.update_progress(f"执行测试用例 {processed_cases}/{total_cases} ({progress_percent:.1f}%)")

            # 获取当前sheet名称
            sheet_name = test_case.get('SheetName', 'Unknown')

            # 当切换到新sheet时打印提示信息
            if sheet_name != current_sheet:
                print(f"{Fore.CYAN}开始执行sheet表: {sheet_name}{Style.RESET_ALL}")
                self.update_progress(f"开始执行sheet表: {sheet_name}")
                current_sheet = sheet_name

            case_id = test_case['测试用例编号']
            if case_id not in case_results:
                case_results[case_id] = {'status': 'success', 'skipped': False}

            # 检查是否执行该用例
            if test_case['是否执行'].lower() != 'y':
                case_results[case_id]['skipped'] = True
                color = Fore.YELLOW
                logging.info(
                    f"{color}测试用例{case_id} - 步骤 {test_case['步骤编号']} [{test_case['测试步骤']}] - 跳过执行")
                continue

            # 执行单个测试用例
            result = self.execute_test_case(test_case)

            # 更新测试结果
            if case_id not in case_results or case_results[case_id]['status'] == 'success':
                if result == 'failure':
                    case_results[case_id]['status'] = 'failure'
                elif result == 'success' and case_results[case_id]['status'] != 'failure':
                    case_results[case_id]['status'] = 'success'

        return case_results

    def _count_test_results(self, case_results):
        """
        统计测试结果：成功、失败和跳过的用例数

        参数:
            case_results (dict): 测试用例执行结果字典

        返回:
            tuple: (成功用例数, 失败用例数, 跳过用例数)
        """
        success_cases = 0
        failed_cases = 0
        skipped_cases = 0
        for case_id, data in case_results.items():
            if data['skipped']:
                skipped_cases += 1
            elif data['status'] == 'success':
                success_cases += 1
            else:
                failed_cases += 1
        return success_cases, failed_cases, skipped_cases

    def _print_test_summary(self, success_cases, failed_cases, skipped_cases):
        """
        打印测试执行摘要信息

        参数:
            success_cases (int): 成功用例数
            failed_cases (int): 失败用例数
            skipped_cases (int): 跳过用例数

        返回:
            None
        """
        print(f"\n{Fore.CYAN}测试执行完成{Style.RESET_ALL}")
        print(f"{Fore.GREEN}成功用例数: {success_cases}{Style.RESET_ALL}")
        print(f"{Fore.RED}失败用例数: {failed_cases}{Style.RESET_ALL}")
        print(f"{Fore.YELLOW}跳过用例数: {skipped_cases}{Style.RESET_ALL}")
        print(f"{Fore.CYAN}总用例数: {success_cases + failed_cases + skipped_cases}{Style.RESET_ALL}")

    def _generate_and_open_report(self, success_cases, failed_cases, skipped_cases, report_title):
        """
        生成并打开 HTML 格式的测试报告

        参数:
            success_cases (int): 成功用例数
            failed_cases (int): 失败用例数
            skipped_cases (int): 跳过用例数
            report_title (str): 报告标题

        返回:
            str: 生成的测试报告文件路径
        """
        total_cases = success_cases + failed_cases + skipped_cases
        report_path = ReportGenerator.generate_html_report(
            self.test_results,
            success_cases,
            failed_cases,
            skipped_cases,
            success_cases + failed_cases + skipped_cases,
            report_title
        )
        print(f"\n{Fore.CYAN}测试报告生成在: {report_path}{Style.RESET_ALL}")
        webbrowser.open(f'file://{os.path.abspath(report_path)}')
        return report_path

    def execute_test_case(self, test_case):
        """
        执行单个测试用例

        参数:
            test_case (Series): 单个测试用例数据

        返回:
            str: 测试结果 ('success', 'failure', 或 'skipped')
        """
        REQUIRED_COLUMNS = [
            '测试用例编号', '测试点', '步骤编号', '测试步骤',
            '是否执行', '测试步骤关键字', '定位方式', '定位地址',
            '参数值', '预期结果'
        ]
        missing_columns = [col for col in REQUIRED_COLUMNS if col not in test_case.index]
        if missing_columns:
            error_msg = f"Excel文件缺少必需的列: {', '.join(missing_columns)}"
            logging.error(error_msg)
            print(f"{Fore.RED}{error_msg}{Style.RESET_ALL}")
            self.update_progress(error_msg)
            return 'failure'

        case_id = test_case['测试用例编号']
        step_number = test_case['步骤编号']
        if test_case['是否执行'].lower() != 'y':
            logging.info(f"用例{case_id}——步骤 {step_number} 设置为不执行，跳过")
            self.update_progress(f"用例{case_id}——步骤 {step_number} 设置为不执行，跳过")
            return 'skipped'

        action_mapping = {
            "点击": lambda: self.click_element(test_case['定位方式'], test_case['定位地址']),
            "等待": lambda: (time.sleep(float(test_case['参数值'])), True)[1],
            "按键": lambda: self.send_keys(test_case['参数值']),
            "输入": lambda: self.input_text(test_case['定位方式'], test_case['定位地址'], test_case['参数值']),
            "验证元素存在": lambda: self.is_element_exist(test_case['定位方式'], test_case['定位地址']),
            "验证Toast存在": lambda: self.is_toast_exist(test_case['参数值']),
            "Toast文本": lambda: self.verify_toast_text(test_case['参数值']),
            "验证元素文本": lambda: self.verify_element_text(test_case['定位方式'], test_case['定位地址'],
                                                             test_case['参数值']),
            "滑动": lambda: self._handle_swipe(test_case['参数值']),
            "输入命令": lambda: self.execute_adb_command(test_case['参数值']),
            "返回桌面": lambda: self.back_to_home()
        }

        success = False
        error_msg = None
        try:
            if test_case['测试步骤关键字'] in action_mapping:
                result = action_mapping[test_case['测试步骤关键字']]()
                if isinstance(result, tuple):
                    success, error_msg = result
                else:
                    success = result
            else:
                error_msg = f"未知操作类型: {test_case['测试步骤关键字']}"
        except Exception as e:
            error_msg = str(e)

        self.print_step_result(case_id, step_number, test_case['测试步骤'], success, error_msg)
        if not success:
            screenshot = self.take_screenshot_as_base64()
        else:
            screenshot = None
        self.test_results.append(TestResult(
            case_id=case_id,
            test_point=test_case['测试点'],
            step_number=step_number,
            step_desc=test_case['测试步骤'],
            status='success' if success else 'failure',
            error_msg=error_msg,
            screenshot=screenshot
        ))

        if success:
            return 'success'
        else:
            return 'failure'

    def _handle_swipe(self, test_data):
        """
        处理滑动操作

        参数:
            test_data (str): 滑动参数字符串，格式为 "start_x,start_y,end_x,end_y,duration"
            start_x,start_y,end_x,end_y：起始点横纵坐标
            duration：持续时长

        返回:
            bool: 滑动操作是否成功
        """
        try:
            start_x, start_y, end_x, end_y, duration = map(float, test_data.split(','))
            return self.swipe_screen(start_x, start_y, end_x, end_y, duration if duration else None)
        except ValueError:
            logging.error("滑动参数格式错误，需要 5 个用逗号分隔的数值。")
            self.update_progress("滑动参数格式错误，需要 5 个用逗号分隔的数值。")
            return False


def save_user_input(config):
    with open("config.json", "w", encoding="utf-8") as f:
        json.dump(config, f, ensure_ascii=False, indent=4)  # 修改: 添加 ensure_ascii=False 参数


def load_user_input():
    if os.path.exists("config.json"):
        try:
            return json.load(open("config.json", encoding="utf-8"))
        except Exception as e:
            # 如果加载失败，可以选择返回空配置
            logging.warning("无法读取 config.json，使用空配置。")
            return None
    return None


def run_tests_in_thread(inputs, progress_callback, completion_callback):
    """在后台线程中运行测试"""

    def test_thread():
        try:
            excel_path, report_title = inputs[-2:]
            config = inputs[:-2]
            test = AndroidUITest()
            test.set_progress_callback(progress_callback)
            test.run_test_suite(excel_path, *config, report_title)
            completion_callback(True)
        except Exception as e:
            progress_callback(f"测试运行出错: {str(e)}")
            completion_callback(False)

    thread = threading.Thread(target=test_thread, daemon=True)
    thread.start()
    return thread


def get_user_input():
    root = tk.Tk()
    root.withdraw()

    config = load_user_input()

    if config and messagebox.askyesno("使用上次配置", "是否使用上次的配置？"):
        return tuple(config.values())

    # 创建主窗口
    input_window = tk.Toplevel(root)
    input_window.title("输入配置项")

    labels = [
        "platformName:",
        "platformVersion:",
        "appPackage:",
        "appActivity:",
        "deviceName:",
        "测试用例表格路径:",
        "报告标题:"
    ]
    entries = []

    for i, label_text in enumerate(labels):
        Label(input_window, text=label_text).grid(row=i, column=0, padx=10, pady=5)
        entry = Entry(input_window, width=50)
        entry.grid(row=i, column=1, padx=10, pady=5)

        # 如果有历史配置则默认填充
        if config:
            entry.insert(0, config.get(label_text.strip(":"), ""))

        entries.append(entry)

    result = None

    def on_submit():
        nonlocal result

        # 检查是否有空字段
        for entry in entries:
            if not entry.get().strip():
                messagebox.showwarning("警告", "请填写所有参数再提交！")
                return

        new_config = dict(zip(
            [label.strip(":") for label in labels],
            [entry.get() for entry in entries]
        ))
        save_user_input(new_config)
        input_window.destroy()
        result = tuple(new_config.values())

    Button(input_window, text="提交", command=on_submit).grid(row=len(labels), column=0, columnspan=2, pady=10)

    root.wait_window(input_window)
    return result


def create_progress_window():
    """创建进度窗口"""
    progress_window = tk.Toplevel()
    progress_window.title("测试进度")
    progress_window.geometry("600x400")

    # 设置窗口在屏幕中央
    progress_window.update_idletasks()
    width = progress_window.winfo_width()
    height = progress_window.winfo_height()
    x = (progress_window.winfo_screenwidth() // 2) - (width // 2)
    y = (progress_window.winfo_screenheight() // 2) - (height // 2)
    progress_window.geometry('{}x{}+{}+{}'.format(width, height, x, y))

    # 创建进度条
    progress_bar = ttk.Progressbar(progress_window, orient="horizontal", length=500, mode="indeterminate")
    progress_bar.pack(pady=20)

    # 创建状态标签
    status_var = tk.StringVar()
    status_var.set("准备中...")
    status_label = Label(progress_window, textvariable=status_var, wraplength=500, justify="left")
    status_label.pack(pady=10)

    # 创建日志文本框
    log_text = tk.Text(progress_window, height=15, width=70)
    log_text.pack(pady=10, padx=10)
    log_text.config(state=tk.DISABLED)

    # 添加关闭按钮
    close_button = Button(progress_window, text="关闭", state=tk.DISABLED, command=progress_window.destroy)
    close_button.pack(pady=10)

    return progress_window, progress_bar, status_var, log_text, close_button


def update_log(log_text, message):
    """更新日志文本框"""
    log_text.config(state=tk.NORMAL)
    log_text.insert(tk.END, message + "\n")
    log_text.see(tk.END)
    log_text.config(state=tk.DISABLED)


def main():
    inputs = get_user_input()
    if inputs:
        # 创建进度窗口
        progress_window, progress_bar, status_var, log_text, close_button = create_progress_window()

        # 定义进度更新回调函数
        def update_progress(message):
            progress_window.after(0, lambda: status_var.set(message))
            progress_window.after(0, lambda: update_log(log_text, message))

        # 定义完成回调函数
        def test_completed(success):
            progress_bar.stop()
            progress_window.after(0, lambda: close_button.config(state=tk.NORMAL))
            if success:
                progress_window.after(0, lambda: status_var.set("测试完成!"))
            else:
                progress_window.after(0, lambda: status_var.set("测试运行出错，请查看日志。"))

        # 启动进度条
        progress_bar.start()

        # 在后台线程中运行测试
        run_tests_in_thread(inputs, update_progress, test_completed)

        # 显示进度窗口
        progress_window.mainloop()
    else:
        print("取消测试")


if __name__ == "__main__":
    main()
